%%-------------------------------------------------------------------
%%% @doc This is the API for Faxien.  
%%% <pre>
%%%  Definitions:
%%%   Guarded Repo - a repository that does not alow direct placement of applications. The main Erlware application would be
%%%                  an example of a guarded repo. You can't just place an app directly into it, apps must go through a process
%%%                  of verification for quality before they are included in the repository.
%%%   Unguarded Repo - a repository that allows direct placement of applications into it.  Most local repo's function this way.
%%% </pre> 
%%% 
%%% Types:
%%%  @type repo() = string(). Contains address and repo designation. 
%%%   Example: http://www.erlware.org/stable   
%%%  @type repo_suffix() = string(). Contains ErtsVsn/Area/Application/Vsn/TarFile.
%%%  @type timeout() = integer() | infinity. Timeouts are specified in milliseconds.
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(faxien).

%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
-include("faxien.hrl").

%%--------------------------------------------------------------------
%% External exports
%%--------------------------------------------------------------------
-export([
	 install_app/3,
	 install_app/2,
	 install_app/1,
	 install/3,
	 install/2,
	 install/1
	]).

-export([
	 list/4,
	 list/2,
	 list/1,
	 list/0,
	 installed/1,
	 installed/0,

	 add_publish_repo/1,
	 remove_publish_repo/1,
	 show_publish_repos/0,

	 set_request_timeout/1,
	 show_request_timeout/0,

	 remove_repo/1,
	 add_repo/1,
	 show_repos/0,

	 help/0,
	 help/1,
	 version/0,

	 describe_app/2,
	 describe_app/1,

	 remove/1, 
	 remove/2,

	 remove_app/1, 
	 remove_app/2
	]).

-export([
	 publish/3,
	 publish/2,
	 publish/1,
	 publish_arch_specific_binary/3,
	 publish_arch_specific_binary/2,
	 publish_arch_specific_binary/1,
	 publish_source/3,
	 publish_source/2,
	 publish_source/1,
	 publish_doc/5,
	 publish_doc/4,
	 publish_doc/3
	]).

-export([
	 upgrade/2,
	 upgrade/0,
	 upgrade/3,
	 upgrade/1,
	 upgrade_app/2,
	 upgrade_app/0,
	 upgrade_app/3,
	 upgrade_app/1
	]).

-export([
	 set_request_timeout_help/0,
	 show_request_timeout_help/0,
	 add_publish_repo_help/0,
	 remove_publish_repo_help/0,
	 show_publish_repos_help/0,
	 remove_repo_help/0,
	 add_repo_help/0,
	 show_repos_help/0,
	 upgrade_app_help/0,
	 upgrade_help/0,
	 install_help/0,
	 install_app_help/0,
	 publish_help/0,
	 list_help/0,
	 installed_help/0,
	 describe_app_help/0,
	 remove_help/0,
	 remove_app_help/0
	]).

%%--------------------------------------------------------------------
%% Macros
%%--------------------------------------------------------------------

%% Absolute default settings.  Config overrides these. 
-define(ERLWARE_URL, "http://www.erlware.org/pub").
-define(STAGING_URL, "http://www.erlware.org/staging").
-define(REQUEST_TIMEOUT, 35000).
-define(IS_LOCAL_BOOT, false).

%% TODO - make this dynamic by using process info
-define(COMMANDS_LIST, [add_repo,
			add_publish_repo,
			describe_app,
			help,
			install_app,
			install,
			installed,
			list,
			publish,
			remove,
			remove_app,
			remove_publish_repo,
			remove_repo,
			show_publish_repos,
			set_request_timeout,
			show_request_timeout,
			show_repos,
			upgrade,
			upgrade_app, 
			version
		       ]).
			

%%====================================================================
%% External functions
%%====================================================================

%%--------------------------------------------------------------------
%% @doc Set repository to publish to.
%% @spec set_request_timeout(Timeout::timeout()) -> ok | {error, Reason}
%%  where
%%   Repo = string() | atom()
%% @end
%%--------------------------------------------------------------------
set_request_timeout(Timeout) when is_integer(Timeout); Timeout == infinity ->
    fax_manage:set_request_timeout(Timeout, fax_paths:installed_config_file_path()).

set_request_timeout_help() ->
    ["\nHelp for set_request_timeout\n",
     "Usage: set_request_timeout <milliseconds> - will set a new timeout value for all remote requests.\n"].

%%--------------------------------------------------------------------
%% @doc Show the repo that faxien is currently configured to publish to.
%% @spec show_request_timeout() -> timeout() | {error, no_request_timeout}
%% @end
%%--------------------------------------------------------------------
show_request_timeout() ->
    {ok, Timeout} = gas:get_env(faxien, request_timeout, {error, no_request_timeout}),
    Timeout.

show_request_timeout_help() ->
    ["\nHelp for show_request_timeout\n",
     "Usage: show_request_timeout - will display the current timeout value for all remote requests.\n"].

%%--------------------------------------------------------------------
%% @doc Remove a repository from the list of repos to fetch from. 
%% @spec remove_publish_repo(Repo) -> ok | {error, Reason}
%%  where
%%   Repo = string() | atom()
%% @end
%%--------------------------------------------------------------------
remove_publish_repo(Repo) when is_atom(Repo)->
    remove_repo(atom_to_list(Repo));
remove_publish_repo(Repo) ->
    fax_manage:remove_repo_to_publish_to(Repo, fax_paths:installed_config_file_path()).

remove_publish_repo_help() ->
    ["\nHelp for remove_publish_repo\n",
     "Usage: remove_publish_repo <url> - will remove one of the repos that faxien publishes packages to.\n",
     "Example: faxien remove_publish_repo http://www.martinjlogan.com:8080/pub"].

%%--------------------------------------------------------------------
%% @doc Add a repository to the list of repos to fetch from. 
%% @spec add_publish_repo(Repo) -> ok | {error, Reason}
%%  where
%%   Repo = string() | atom()
%% @end
%%--------------------------------------------------------------------
add_publish_repo(Repo) when is_atom(Repo)->
    add_publish_repo(atom_to_list(Repo));
add_publish_repo(Repo) ->
    fax_manage:add_repo_to_publish_to(Repo, fax_paths:installed_config_file_path()).
    
add_publish_repo_help() ->
    ["\nHelp for add_publish_repo\n",
     "Usage: add_publish_repo <url> - will add a repo to the list of repos that faxien publishes packages to.\n",
     "Example: faxien add_publish_repo http://www.martinjlogan.com:8080/pub"].

%%--------------------------------------------------------------------
%% @doc Show the list of publish repos that faxien is currently configured to publish to.
%% @spec show_publish_repos() -> list()
%% @end
%%--------------------------------------------------------------------
show_publish_repos() ->
    {ok, Repos} = gas:get_env(faxien, repos_to_publish_to, []),
    Repos.
								   
show_publish_repos_help() ->
    ["\nHelp for show_publish_repos\n",
     "Usage: show_publish_repos - display the list of repos that faxien publishes packages to.\n"].


%%--------------------------------------------------------------------
%% @doc Remove a repository from the list of repos to fetch from. 
%% @spec remove_repo(Repo) -> ok | {error, Reason}
%%  where
%%   Repo = string() | atom()
%% @end
%%--------------------------------------------------------------------
remove_repo(Repo) when is_atom(Repo)->
    remove_repo(atom_to_list(Repo));
remove_repo(Repo) ->
    fax_manage:remove_repo_to_fetch_from(Repo, fax_paths:installed_config_file_path()).

remove_repo_help() ->
    ["\nHelp for remove_repo\n",
     "Usage: remove_repo <url> - will remove one of the repos that faxien pulls packages from.\n",
     "Example: faxien remove_repo http://www.martinjlogan.com:8080/pub"].

%%--------------------------------------------------------------------
%% @doc Add a repository to the list of repos to fetch from. 
%% @spec add_repo(Repo) -> ok | {error, Reason}
%%  where
%%   Repo = string() | atom()
%% @end
%%--------------------------------------------------------------------
add_repo(Repo) when is_atom(Repo)->
    add_repo(atom_to_list(Repo));
add_repo(Repo) ->
    fax_manage:add_repo_to_fetch_from(Repo, fax_paths:installed_config_file_path()).
    
add_repo_help() ->
    ["\nHelp for add_repo\n",
     "Usage: add_repo <url> - will add a repo to the list of repos that faxien pulls packages from.\n",
     "Example: faxien add_repo http://www.martinjlogan.com:8080/pub"].

%%--------------------------------------------------------------------
%% @doc Show the list of repos that faxien is currently configured to fetch from.
%% @spec show_repos() -> list()
%% @end
%%--------------------------------------------------------------------
show_repos() ->
    {ok, Repos} = gas:get_env(faxien, repos_to_fetch_from, []),
    Repos.
								   
show_repos_help() ->
    ["\nHelp for show_repos\n",
     "Usage: show_repos - display the list of repos that faxien pulls packages from.\n"].

%%--------------------------------------------------------------------
%% @doc upgrade a single application.
%% <pre>
%% Examples:
%%  upgrade_app(["http://erlware.org/stable", "http://erlwaremirror.org/stable"], faxien, "usr/local/erlware").
%% or
%%  upgrade_app('http://erlware.org/stable', faxien, "usr/local/erlware").
%% </pre>
%% @spec upgrade_app(Repos, AppName, InstallationPath) -> ok | {error, Reason}
%%  where
%%   Repos = [string()] | atom()
%%   AppName = string() | atom()
%%   InstallationPath = string() | atom()
%% @end
%%--------------------------------------------------------------------
upgrade_app(Repo, AppName, InstallationPath) when is_atom(Repo) -> 
    upgrade_app([atom_to_list(Repo)], AppName, InstallationPath);
upgrade_app(Repos, AppName, InstallationPath) -> 
    [A,B] = fax_util:if_atom_to_string([AppName, InstallationPath]),
    fax_manage:upgrade_application(Repos, A, B).

%% @spec upgrade_app(AppName) -> ok | {error, Reason}
%% @equiv upgrade_app(Repos, AppName, InstallationPath)
upgrade_app(AppName) -> 
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    {ok, Repos}            = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    upgrade_app(Repos, AppName, InstallationPath).

    
    

%%--------------------------------------------------------------------
%% @doc upgrade a all installed applications.
%% <pre>
%% Examples:
%%  upgrade_app(["http://erlware.org/stable", "http://erlwaremirror.org/stable"], "usr/local/erlware").
%% or
%%  upgrade_app('http://erlware.org/stable', "usr/local/erlware").
%% </pre>
%% @spec upgrade_app(Repos, InstallationPath) -> ok | {error, Reason}
%%  where
%%   Repos = [string()] | atom()
%%   InstallationPath = string() | atom()
%% @end
%%--------------------------------------------------------------------
upgrade_app(Repo, InstallationPath) when is_atom(Repo) ->
    upgrade_app([atom_to_list(Repo)], InstallationPath);
upgrade_app(Repos, InstallationPath) ->
    [A] = fax_util:if_atom_to_string([InstallationPath]),
    fax_manage:upgrade_applications(Repos, A).

upgrade_app() -> 
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    {ok, Repos}            = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    upgrade_app(Repos, InstallationPath).

upgrade_app_help() ->
    ["\nHelp for upgrade_app\n",
     "upgrade_app: will upgrade all installed applications" ,
     "upgrade_app <app name>: will upgrade an installed application"]. 

%%--------------------------------------------------------------------
%% @doc upgrade a single release.
%% <pre>
%% Examples:
%%  upgrade(["http://erlware.org/stable", "http://erlwaremirror.org/stable"], faxien, "usr/local/erlware").
%% or
%%  upgrade('http://erlware.org/stable', faxien, "usr/local/erlware").
%% </pre>
%% @spec upgrade(Repos, RelName, InstallationPath) -> ok | {error, Reason}
%%  where
%%   Repos = [string()] | atom()
%%   RelName = string() | atom()
%%   InstallationPath = string() | atom()
%% @end
%%--------------------------------------------------------------------
upgrade(Repo, RelName, InstallationPath) when is_atom(Repo) -> 
    upgrade([atom_to_list(Repo)], RelName, InstallationPath);
upgrade(Repos, RelName, InstallationPath) -> 
    [A,B]             = fax_util:if_atom_to_string([RelName, InstallationPath]),
    {ok, IsLocalBoot} = gas:get_env(faxien, is_local_boot, [?IS_LOCAL_BOOT]),
    fax_manage:upgrade_release(Repos, A, B, IsLocalBoot).

%% @spec upgrade(RelName) -> ok | {error, Reason}
%% @equiv upgrade(Repos, RelName, InstallationPath)
upgrade(RelName) -> 
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    {ok, Repos}            = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    upgrade(Repos, RelName, InstallationPath).

%%--------------------------------------------------------------------
%% @doc upgrade a all installed releases.
%% <pre>
%% Examples:
%%  upgrade(["http://erlware.org/stable", "http://erlwaremirror.org/stable"], "usr/local/erlware").
%% or
%%  upgrade('http://erlware.org/stable', "usr/local/erlware").
%% </pre>
%% @spec upgrade(Repos, InstallationPath) -> ok | {error, Reason}
%%  where
%%   Repos = [string()] | atom()
%%   InstallationPath = string() | atom()
%% @end
%%--------------------------------------------------------------------
upgrade(Repo, InstallationPath) when is_atom(Repo) ->
    upgrade([atom_to_list(Repo)], InstallationPath);
upgrade(Repos, InstallationPath) ->
    [A]               = fax_util:if_atom_to_string([InstallationPath]),
    {ok, IsLocalBoot} = gas:get_env(faxien, is_local_boot, [?IS_LOCAL_BOOT]),
    fax_manage:upgrade_releases(Repos, A, IsLocalBoot).

%% @spec upgrade() -> ok | {error, Reason}
%% @equiv upgrade(Repos, InstallationPath)
upgrade() -> 
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    {ok, Repos}            = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    upgrade(Repos, InstallationPath).

upgrade_help() ->
    ["\nHelp for upgrade\n",
     "upgrade: will upgrade all installed releases" ,
     "upgrade <release name>: will upgrade an installed release"]. 


%%--------------------------------------------------------------------
%% @doc 
%%  Install a release and all its applications from a repository. This function will pull down the release tarball for the 
%%  specified application unpack it, pull down all the applications specified by the included .rel file and finally build
%%  a local .boot file used for startup.  
%% @spec install(Repos, ReleaseName, ReleaseVsn) -> ok | {error, Reason}
%% where
%%     Repos = [string()] | [atom()] | atom()
%%     ReleaseName = string() | atom()
%%     ReleaseVsn = 'LATEST' | string() | atom()
%% @end
%%--------------------------------------------------------------------
install(Repos, ReleaseName, ReleaseVsn) when is_atom(Repos)  -> 
    install([atom_to_list(Repos)], ReleaseName, ReleaseVsn);
install(Repos, ReleaseName, ReleaseVsn)  -> 
    ?INFO_MSG("faxien:install(~p, ~p, ~p)~n", [Repos, ReleaseName, ReleaseVsn]),
    % Any atoms must be turned to strings.  Atoms are accepted because it makes
    % the invocation from the command line cleaner. 
    [A,B]                  = fax_util:if_atom_to_string([ReleaseName, ReleaseVsn]),
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    {ok, IsLocalBoot}      = gas:get_env(faxien, is_local_boot, [?IS_LOCAL_BOOT]),
    fax_install:install_remote_release(Repos, A, B, InstallationPath, IsLocalBoot).

%% @spec install(ReleaseName, ReleaseVsn) -> ok | {error, Reason}
%% @equiv install(ERLWARE, ReleaseName, ReleaseVsn)
install(ReleaseName, ReleaseVsn) -> 
    {ok, Repos} = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    install(Repos, ReleaseName, ReleaseVsn).

%%--------------------------------------------------------------------
%% @doc 
%%  This function will determine if the release to be installed is a local release package or it is a package that must
%%  first be pulled down from a remote repo and pulled down.  If the package is local then it will be installed into the 
%%  configured location.  If the package is remote than the latest version of that package will be pulled down and installed. 
%%
%% @spec install(ReleaseNameOrPath) -> ok | {error, Reason}
%%  where
%%   ReleaseNameOrPath = atom() | string()
%% @end
%%--------------------------------------------------------------------
install(ReleaseNameOrPath) when is_atom(ReleaseNameOrPath) -> 
    install(atom_to_list(ReleaseNameOrPath));
install(ReleaseNameOrPath) -> 
    {ok, Repos}       = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    {ok, IsLocalBoot} = gas:get_env(faxien, is_local_boot, [?IS_LOCAL_BOOT]),
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    fax_install:install_release(Repos, ReleaseNameOrPath, InstallationPath, IsLocalBoot).
	
    
install_help() ->
    ["\nHelp for install\n",
     "Usage: install <release name|release tarball> [release version]: will install a release remotely or from a local package depending on its argument\n"]. 


%%--------------------------------------------------------------------
%% @doc 
%%  Install an application from a repository.
%% <pre>
%% Examples:
%%  install_app(["http"//www.erlware.org/pub"], gas, "4.6.0")
%%  install_app(["http"//www.erlware.org/pub"], gas, "LATEST")
%% </pre>
%% @spec install_app(Repos, AppName, AppVsn) -> ok | {error, Reason}
%% where
%%     Repos = [string()] | [atom()] | atom()
%%     AppName = string() | atom()
%%     AppVsn = 'LATEST' | string() | atom()
%% @end
%%--------------------------------------------------------------------
install_app(Repos, AppName, AppVsn) when is_atom(Repos)  -> 
    install_app([atom_to_list(Repos)], AppName, AppVsn);
install_app(Repos, AppName, AppVsn)  -> 
    % Any atoms must be turned to strings.  Atoms are accepted because it makes
    % the invocation from the command line cleaner. 
    [A,B] = fax_util:if_atom_to_string([AppName, AppVsn]),
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    fax_install:install_remote_application(Repos, A, B, InstallationPath).

%% @spec install_app(AppName, AppVsn) -> ok | {error, Reason}
%% @equiv install_app(ERLWARE, AppName, AppVsn)
install_app(AppName, AppVsn) -> 
    {ok, Repos} = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    install_app(Repos, AppName, AppVsn).

%%--------------------------------------------------------------------
%% @doc 
%%  This function will determine if the application to be installed is a local release package or it is a package that must
%%  first be pulled down from a remote repo and pulled down.  If the package is local then it will be installed into the 
%%  configured location.  If the package is remote than the latest version of that package will be pulled down and installed. 
%%
%% @spec install_app(AppNameOrPath) -> ok | {error, Reason}
%%  where
%%   AppNameOrPath = atom() | string()
%% @end
%%--------------------------------------------------------------------
install_app(AppNameOrPath) when is_atom(AppNameOrPath) -> 
    install_app(atom_to_list(AppNameOrPath));
install_app(AppNameOrPath) -> 
    {ok, Repos}            = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    fax_install:install_application(Repos, AppNameOrPath, InstallationPath).

install_app_help() ->
    ["\nHelp for install_app\n",
     "Usage: install_app <app name|app tarball> [app version]: will install an OTP app remotely or from a local package depending on its argument\n"]. 


%%--------------------------------------------------------------------
%% @doc 
%%  Publishes a pre-built generic application or a release to a remote unguarded repository. A generic application 
%%  typically consists of erlang object code and possibly other platform independent code.  
%%  This code is then available for immediate use by any application is erlware repo compatible such as Sinan.
%%
%% @spec publish(Repos, AppDir, Timeout) -> ok | {error, Reason}
%% where
%%     Repo = string() | atom()
%%     AppDir = string() | atom()
%%     Timeout = timeout()
%% @end
%%--------------------------------------------------------------------
publish(Repo, PackageDir, Timeout) -> 
    [A,B] = fax_util:if_atom_to_string([Repo, PackageDir]),
    fax_publish:publish([A], B, Timeout).

%% @spec publish(PackageDir, Timeout) -> ok | {error, Reason}
%% @equiv publish(Repos AppDir, Timeout)
publish(PackageDir, Timeout) when is_integer(Timeout); Timeout == infinity -> 
    {ok, Repos} = gas:get_env(faxien, repos_to_publish_to, ?ERLWARE_URL),
    [A]         = fax_util:if_atom_to_string([PackageDir]),
    fax_publish:publish(Repos, A, Timeout);

%% @spec publish(Repos PackageDir) -> ok | {error, Reason}
%% @equiv publish(Repos AppDir, Timeout)
publish(Repo, PackageDir) -> 
    {ok, Timeout} = gas:get_env(faxien, request_timeout, ?REQUEST_TIMEOUT),
    publish([Repo], PackageDir, Timeout).

%% @spec publish(AppDir) -> ok | {error, Reason}
%% @equiv publish(DefaultRepos AppDir)
publish(PackageDir) -> 
    {ok, Timeout} = gas:get_env(faxien, request_timeout, ?REQUEST_TIMEOUT),
    publish(PackageDir, Timeout).

publish_help() ->
    ["\nHelp for publish\n",
     "Usage: publish <release package|app package>: will publish an erts, release, or application package. Faxien figures out which based on inspection.\n",
     "Packages should be of the form <package name>-<package version>[.tar.gz] for example erts-5.5.5.tar.gz"]. 



%%--------------------------------------------------------------------
%% @doc 
%%  Publishes an architecture specific pre-built applications to a remote unguarded repository. This will publish a pre-built 
%%  application into a directory &lt;arch&gt; within a repo that is named after the architecture from which the application is 
%%  published from. This code is then available for immediate use by any application that uses erlware ewrepo code such as Sinan.
%%
%% @spec publish_arch_specific_binary(Repo, AppDir, Timeout) -> ok | {error, Reason}
%% where
%%     Repo = string() | atom()
%%     AppDir = string() | atom()
%%     Timeout = timeout()
%% @end
%%--------------------------------------------------------------------
publish_arch_specific_binary(Repo, AppDir, Timeout) -> 
    [A,B] = fax_util:if_atom_to_string([Repo, AppDir]),
    fax_publish:publish(binary, A, B, Timeout).

%% @spec publish_arch_specific_binary(AppDir, Timeout::timeout()) -> ok | {error, Reason}
%% @equiv publish_arch_specific_binary(DefaultRepo, AppDir, Timeout)
publish_arch_specific_binary(AppDir, Timeout) when is_integer(Timeout); Timeout == infinity -> 
    {ok, Repo} = gas:get_env(faxien, repo_to_publish_to, ?ERLWARE_URL),
    publish_arch_specific_binary(Repo, AppDir, Timeout); 

%% @spec publish_arch_specific_binary(Repo, AppDir) -> ok | {error, Reason}
%% @equiv publish_arch_specific_binary(Repo, AppDir, Timeout)
publish_arch_specific_binary(Repo, AppDir) -> 
    {ok, Timeout} = gas:get_env(faxien, request_timeout, ?REQUEST_TIMEOUT),
    publish_arch_specific_binary(Repo, AppDir, Timeout). 

%% @spec publish_arch_specific_binary(AppDir) -> ok | {error, Reason}
%% @equiv publish_arch_specific_binary(AppDir, Timeout)
publish_arch_specific_binary(AppDir) -> 
    {ok, Timeout} = gas:get_env(faxien, request_timeout, ?REQUEST_TIMEOUT),
    publish_arch_specific_binary(AppDir, Timeout). 

%%--------------------------------------------------------------------
%% @doc 
%%  Publishes an unbuilt application containing source code to a remote unguarded repository. This will publish source code 
%%  into the Source area within a repo. This code is then available for immediate use by any application that uses erlware 
%%  ewrepo code such as Sinan.
%%
%% @spec publish_source(Repo, AppDir, Timeout) -> ok | {error, Reason}
%% where
%%     Repo = string() | atom()
%%     AppDir = string() | atom()
%%     Timeout = timeout()
%% @end
%%--------------------------------------------------------------------
publish_source(Repo, AppDir, Timeout) -> 
    [A,B] = fax_util:if_atom_to_string([Repo, AppDir]),
    fax_publish:publish(source, A, B, Timeout).

%% @spec publish_source(AppDir, Timeout) -> ok | {error, Reason}
%% @equiv publish_source(DefaultRepo, AppDir, Timeout)
publish_source(AppDir, Timeout) when is_integer(Timeout); Timeout == infinity -> 
    {ok, Repo} = gas:get_env(faxien, repo_to_publish_to, ?ERLWARE_URL),
    publish_source(Repo, AppDir, Timeout); 

%% @spec publish_source(Repo, AppDir) -> ok | {error, Reason}
%% @equiv publish_source(Repo, AppDir, Timeout)
publish_source(Repo, AppDir) -> 
    {ok, Timeout} = gas:get_env(faxien, request_timeout, ?REQUEST_TIMEOUT),
    publish_source(Repo, AppDir, Timeout). 

%% @spec publish_source(AppDir) -> ok | {error, Reason}
%% @equiv publish_source(AppDir, Timeout)
publish_source(AppDir) -> 
    {ok, Timeout} = gas:get_env(faxien, request_timeout, ?REQUEST_TIMEOUT),
    publish_source(AppDir, Timeout). 


%%--------------------------------------------------------------------
%% @doc 
%%  Publishes documentation for the application to a remote unguarded repo.
%% @spec publish_doc(Repo, AppName, AppVsn, DocDir, Timeout) -> ok | {error, Reason}
%% where
%%     Repo = string() | atom()
%%     AppName = string() | atom()
%%     AppVsn = string() | atom()
%%     DocDir = string() | atom()
%%     Timeout = timeout()
%% @end
%%--------------------------------------------------------------------
publish_doc(Repo, AppName, AppVsn, DocDir, Timeout) -> 
    [A,B,C,D] = fax_util:if_atom_to_string([Repo, AppName, AppVsn, DocDir]),
    fax_publish:publish(doc, A, B, C, D, Timeout).

%% @spec publish_doc(AppName, AppVsn, DocDir, Timeout) -> ok | {error, Reason}
%% @equiv publish_doc(DefaultRepo, AppName, AppVsn, DocDir, Timeout)
publish_doc(AppName, AppVsn, DocDir, Timeout) -> 
    {ok, Repo} = gas:get_env(faxien, repo_to_publish_to, ?ERLWARE_URL),
    publish_doc(Repo, AppName, AppVsn, DocDir, Timeout).

%% @spec publish_doc(AppName, AppVsn, DocDir) -> ok | {error, Reason}
%% @equiv publish_doc(AppName, AppVsn, DocDir, Timeout)
publish_doc(AppName, AppVsn, DocDir) -> 
    {ok, Timeout} = gas:get_env(faxien, request_timeout, ?REQUEST_TIMEOUT),
    publish_doc(AppName, AppVsn, DocDir, Timeout).

%%--------------------------------------------------------------------
%% @doc 
%%  Return the version of the current Faxien release.
%% @spec version() -> string()
%% @end
%%--------------------------------------------------------------------
version() -> 
    %% @todo this should look at the app version dynamically by looking at what verion has been loaded.
    "0.20.6".

%%--------------------------------------------------------------------
%% @doc 
%%  Print the help screen
%% @spec help() -> ok
%% @end
%%--------------------------------------------------------------------
help() -> 
    io:format("~nFaxien Commands:~n"),
    print_help_list(?COMMANDS_LIST),
    print_help_list(["\nFor information on individual commands type faxien help <command name>\n",
		    "Examples:\n",
		    "publish the tools application with a timeout of infinity: faxien publish /usr/local/erlang/lib/tools-2.5.4 infinity",
		    "install the tools application from the local filesystem: faxien install_app /usr/local/erlang/lib/tools-2.5.4",
		    "install sinan from a remote repository: faxien install sinan",
		    "install sinan version 0.8.8 from a remote repository: faxien install sinan 0.8.8",
		    "install a new version of faxien from a release tarball: faxien install faxien-0.19.3.tar.gz",
		    "add an additional repo to publish to: faxien add_publish_repo http://www.martinjlogan.com:8080/pub"]).  


%%--------------------------------------------------------------------
%% @doc 
%%  Print the help screen for a specific command.
%% @spec help(Command::atom()) -> ok
%% @end
%%--------------------------------------------------------------------
help(Command) when is_atom(Command) ->
    help_for_command(Command).

%%--------------------------------------------------------------------
%% @doc 
%%  Returns a list of packages.
%% @spec list(Repos, Side, SearchType, SearchString) -> string()
%%  where
%%   Repos = list()
%%   Side = lib | releases | both
%%   SearchType = regexp | normal
%%   SearchString = string() | atom()
%% @end
%%--------------------------------------------------------------------
list(Repos, Side, SearchType, SearchString) when is_atom(SearchString) -> 
    list(Repos, Side, SearchType, atom_to_list(SearchString));
list(Repos, Side, SearchType, SearchString) -> 
    fax_manage:list(Repos, Side, SearchType, SearchString).

list(SearchType, SearchString) -> 
    {ok, Repos} = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    list(Repos, both, SearchType, SearchString).

list(SearchString) -> 
    {ok, Repos} = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    list(Repos, both, normal, SearchString).
    
list() -> 
    {ok, Repos} = gas:get_env(faxien, repos_to_fetch_from, [?ERLWARE_URL]),
    list(Repos, both, normal, "").

list_help() ->
    ["\nHelp for list\n",
     "Usage: list [[space separated repos] [both|lib|releases] [normal|regexp] search_string]: lists the contents of a repository in various ways.\n",
     "Example: list lib regexp std.* - this example will list all libraries (lib) that match the regexp std.*" ,
     "Example: list regexp std.* - this example will list all libraries and releases that match the regexp std.*" ,
     "Example: list yaw - this example will list all libraries and releases that contain the string 'yaw'" ,
     "Example: list - this example will list all libraries and releases"].


%%--------------------------------------------------------------------
%% @doc 
%%  Returns a list of packages that are currently installed.
%% @spec installed(InstallationPath, Side) -> string()
%%  where
%%   Side = lib | releases | both
%% @end
%%--------------------------------------------------------------------
installed(Side) when Side == lib; Side == releases; Side == both ->
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    fax_manage:installed(InstallationPath, Side).

%% @equiv installed(InstallationPath, both)
%% @spec installed(InstallationPath) -> string()
installed() ->
    installed(both).

installed_help() ->
    ["\nHelp for installed\n",
     "Usage: list [both|applications|releases]: lib will list all installed applications, releases all installed releases, and both will list both\n",
     "Example: list - will list all installed applications and releases.",
     "\nnote: The use of side = releases | lib is historical.  Applications are installed into erlang under lib and releases under releases.",
     "hence the somewhat odd names :). Perhaps someone will complain a lot and we will write a translator."].

%%--------------------------------------------------------------------
%% @doc 
%%  Remove an installed application.
%% @spec remove_app(InstallationPath, AppName, AppVsn) -> ok
%%  where
%%   AppName = string()
%%   AppVsn = string()
%% @end
%%--------------------------------------------------------------------
remove_app(AppName, AppVsn) ->
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    [A,B] = fax_util:if_atom_to_string([AppName, AppVsn]),
    fax_manage:remove_app(InstallationPath, A, B). 

%%--------------------------------------------------------------------
%% @doc 
%%  Remove all versions of an installed application.
%% @spec remove_app(InstallationPath, AppName) -> ok
%%  where
%%   AppName = string()
%% @end
%%--------------------------------------------------------------------
remove_app(AppName) ->
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    [A] = fax_util:if_atom_to_string([AppName]),
    fax_manage:remove_app(InstallationPath, A).

remove_app_help() ->
    ["\nHelp for remove_app\n",
     "Usage: remove_app <app name> [app version]: remove a particular application for all versions or a particular version.\n",
     "Example: remove_app tools - removes all versions of the tools application currently installed.",
     "Example: remove_app tools 2.4.5 - removes version 2.4.5 of the tools version."].

%%--------------------------------------------------------------------
%% @doc 
%%  Remove an installed release.
%% @spec remove(InstallationPath, RelName, RelVsn) -> ok
%%  where
%%   RelName = string()
%%   RelVsn = string()
%% @end
%%--------------------------------------------------------------------
remove(RelName, RelVsn) ->
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    [A,B] = fax_util:if_atom_to_string([RelName, RelVsn]),
    fax_manage:remove_release(InstallationPath, A, B, false). 

%%--------------------------------------------------------------------
%% @doc 
%%  Remove all versions of an installed release.
%% @spec remove(InstallationPath, RelName) -> ok
%%  where
%%   RelName = string()
%% @end
%%--------------------------------------------------------------------
remove(RelName) ->
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    [A] = fax_util:if_atom_to_string([RelName]),
    fax_manage:remove_release(InstallationPath, A, false).

remove_help() ->
    ["\nHelp for remove\n",
     "Usage: remove <release name> [release version]: remove all versions or a specific version of a particular release.\n",
     "Example: remove sinan - removes all versions of the sinan release that are currently installed.",
     "Example: remove sinan 0.8.8 - removes version 0.8.8 of the sinan release."].

%%--------------------------------------------------------------------
%% @doc 
%%  Fetch the description for a particular application.
%% @spec describe_app(AppName, AppVsn) -> ok
%%  where
%%   AppName = string() | atom()
%%   AppVsn = string() | atom()
%% @end
%%--------------------------------------------------------------------
describe_app(AppName, AppVsn) ->
    [A, B]        = fax_util:if_atom_to_string([AppName, AppVsn]),
    TargetErtsVsn = ewr_util:erts_version(),
    {ok, Repos}   = gas:get_env(faxien, repos_to_fetch_from, []),
    fax_manage:describe_app(Repos, TargetErtsVsn, A, B).

%%--------------------------------------------------------------------
%% @doc 
%%  Fetch the description for the highest vesion available for a particular application.
%% @spec describe_app(AppName) -> ok
%%  where
%%   AppName = string() | atom()
%% @end
%%--------------------------------------------------------------------
describe_app(AppName) ->
    [A]           = fax_util:if_atom_to_string([AppName]),
    TargetErtsVsn = ewr_util:erts_version(),
    {ok, Repos}   = gas:get_env(faxien, repos_to_fetch_from, []),
    fax_manage:describe_app(Repos, TargetErtsVsn, A).

describe_app_help() ->
    ["\nHelp for describe_app\n",
     "Usage: describe_app <application name> [app version]: Fetch the description for a particular application.\n",
     "Example: describe_app sinan - Bring back a description of the highest version available for Sinan.",
     "Example: describe_app sinan 0.8.8 - Bring back a description of version 0.8.8 of the Sinan application."].

%%====================================================================
%% Internal functions
%%====================================================================


help_for_command(Command) ->
    StrCommand = atom_to_list(Command),
    Func       = list_to_atom(StrCommand ++ "_help"),
    case catch ?MODULE:Func() of
	{'EXIT', _Reason} ->
	    io:format("That command does not have detailed help associated with it~n");
	HelpList -> 
	    print_help_list(HelpList) 
    end.

print_help_list(HelpList) ->	   
    lists:foreach(fun(HelpString) -> io:format("~s~n", [HelpString]) end, HelpList).
    
    
